/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cn.tedu.mall.sso.service.impl;

import cn.tedu.mall.common.config.CoolSharkStaticPrefixConfiguration;
import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.sso.service.ICodeService;
import com.alibaba.druid.util.StringUtils;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

@Slf4j
abstract class AbstractCodeService implements ICodeService {

    private StringRedisTemplate redisTemplate;

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    protected void lockCheck(String phoneNum) {
        String key = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.LOCK_SMS_PREFIX + phoneNum;
        Boolean exist = redisTemplate.hasKey(key);
        if (exist) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您的电话号码已经被锁定5分钟");
        }
    }

    protected void validatePhone(String phoneNum) {
        String regex = "^1[34589][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$";
        if (!(phoneNum.matches(regex))) {
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "你输入的电话号码不正确");
        }
    }

    protected String generateCode(String phoneNum) {
        //验证规则是通过list验证
        String countKey = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.COUNT_SMS_PREFIX + phoneNum;
        String listKey = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.LOCK_SMS_SHOULD_PREFIX + phoneNum;
        String lockKey = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.LOCK_SMS_PREFIX + phoneNum;
        String codeKey = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.CODE_SMS_PREFIX + phoneNum;
        ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        ListOperations<String, String> opsForList = redisTemplate.opsForList();
        LocalDateTime now = LocalDateTime.now();
        //当前系统时间戳毫秒数
        long nowTimeStamp = now.atZone(ZoneId.of("+8")).toInstant().toEpochMilli();
        //所有一下操作都是在count数量正常的情况下
        Long count = opsForValue.increment(countKey);
        if (count > 4) {
            //锁定
            Set<String> keys = new HashSet<>();
            keys.add(countKey);
            keys.add(listKey);
            keys.add(codeKey);
            redisTemplate.delete(keys);
            redisTemplate.opsForValue().set(lockKey, "", 5, TimeUnit.MINUTES);
            log.info("由于超出次数被锁定");
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您的手机发送验证码太频繁，被锁定5分钟");
        } else if (count == 4) {
            //说明这是第四次访问，需要验证时间差list中应该存在3个发送时间点
            //往里放是rpush 向外拿是lpop
            String firstTimeStampStr = opsForList.leftPop(listKey);
            //转化Long
            long firstTimeStamp = Long.parseLong(firstTimeStampStr);
            if ((nowTimeStamp - firstTimeStamp) < 1000 * 60 * 5) {
                //违反了规则锁定
                Set<String> keys = new HashSet<>();
                keys.add(countKey);
                keys.add(listKey);
                keys.add(codeKey);
                redisTemplate.delete(keys);
                redisTemplate.opsForValue().set(lockKey, "", 5, TimeUnit.MINUTES);
                log.info("由于第四次与第一次时间将在5分钟之内被锁定");
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST, "您的手机发送验证码太频繁，被锁定5分钟");
            }
        }
        //生成code
        String code = "";
        if (redisTemplate.hasKey(codeKey)) {
            code = opsForValue.get(codeKey);
            throw new CoolSharkServiceException(ResponseCode.CONFLICT,"您上次发送的验证码还未使用");
        } else {
            code = new Random().nextInt(9000) + 1000 + "";
        }
        //code在15分钟之内可用
        opsForList.rightPush(listKey, nowTimeStamp + "");
        opsForValue.set(codeKey, code, 15, TimeUnit.MINUTES);
        return code;
    }

    protected void checkValidCode(String validCode, String validCodeId, String ip) {

        String validCodeKey = CoolSharkStaticPrefixConfiguration.SSOPrefixConfiguration.VALID_CODE_PREFIX + ip + ":" + validCodeId;
        log.info("远程ip地址:{}", ip);
        ValueOperations operations = redisTemplate.opsForValue();
        String text = (String) operations.get(validCodeKey);
        log.info("从redis获取validCode:{}",text);
        log.info("传入validCode:{}",validCode);
        log.info("两者相等:{}",StringUtils.equalsIgnoreCase(text, validCode));
        if (StringUtils.isEmpty(text)) {
            throw new CoolSharkServiceException(ResponseCode.FORBIDDEN, "请先生成验证码");
        }
        if (!StringUtils.equalsIgnoreCase(text, validCode)) {
            throw new CoolSharkServiceException(ResponseCode.FORBIDDEN, "验证码不正确");
        }
        redisTemplate.delete(validCodeKey);
    }
}
